/*******************************************************************
Leecher:
Overview:
To be called by manager, when updater returns with a seeder.
Arguments:
@filename:
@part_num:
@pointer to struct seeder:
********************************************************************/
#include "dstruct.h"
#include "header.h"
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/signal.h>
#include<sys/time.h>
#include<sys/resource.h>
#include<sys/wait.h>
#include<sys/errno.h>
#include<netinet/in.h>
#include<netdb.h>
#include<pthread.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>

#define BUFFSIZE 1024

#define DEBUG

static int seq_no=0; // msg seq number

int random_select(int max_pieces)
{
	static long a = 3003003;
	a = (a*250)%29763027;
	return ((a%max_pieces));
	
}

void send_update2tracker(char * trackerIP,int trackerport,char *filename,int part_num,int peer_id)
{

			struct sockaddr_in trackersock_in;
	 int sock, sinsize;
	 char send_buffer[SOCK_BUFF]={0};
	
	 char receive_buffer[SOCK_BUFF]={0};
	 sinsize = sizeof(trackersock_in);
	 	 memset(&trackersock_in, 0, sizeof(trackersock_in));
	 trackersock_in.sin_family = AF_INET; 

		
		inet_pton(AF_INET,trackerIP, &(trackersock_in.sin_addr));
	  trackersock_in.sin_port=htons(trackerport);
		
		memset(&send_buffer,0,SOCK_BUFF);
  		sprintf(send_buffer,"1$%s$%d$%d$%d",filename,part_num,peer_id,SEEDERSERVERPORT); // 0x02 protocol field
#ifdef DEBUG
		printf("\nThe 0x01 Buffer is %s ",send_buffer);
		fflush(stdout);
#endif
 		 /*create socket*/
		if((sock=socket(PF_INET,SOCK_STREAM,0))<0){
			printf("\nFailed to create socket ... \n");
			exit(1);
		}

#ifdef DEBUG
		printf("\nConnecting to Tracker at IP: %s , Port %d ",trackerIP,trackerport);
		fflush(stdout);
#endif
		/*Connect to server*/
		if(connect(sock,(struct sockaddr *)&trackersock_in,sizeof(trackersock_in)) <0){
			printf("\nFailed to connect to tracker ... \n");
			exit(1);		
		}
		/*Send protocol to Tracker*/
		
		send(sock,send_buffer,sizeof(send_buffer),0);
 #ifdef DEBUG
		printf("\n0x01 protocol Buffer Sent ...");
		fflush(stdout);
#endif
	close(sock);


}

void update_listds(struct file_part *head, char *receive_buffer)
{
		unsigned int part_num=0;
		struct file_part *temp=head;
		int seederindex=0;
		char * field,*fieldvalue,*subfield,*subfieldvalue;
    char seederip[30];
    int seeder_port;
  	int i=0,node_number=0;
  	int noseederflag=0;
	int peer_id;	
	char filename[BUFF]={0};
	char tempbuffer[BUFFSIZE]={0};
	char tempbuffer2[BUFFSIZE]={0};
	
		field=strtok(receive_buffer,"$");// protocol
		
		fieldvalue=strtok(NULL,"$"); //seq_no
		fieldvalue=strtok(NULL,"$"); //filename
		strcpy(filename,fieldvalue);
		fieldvalue=strtok(NULL,"$"); //partnumber this msg is for..
		sscanf (fieldvalue, "%d", &part_num);
		printf("\nPart Number:%d",part_num);
		fieldvalue=strtok(NULL,"$"); //peer_id:IPaddr:port,peer_id:IPaddr:port ........ based on existing no. of seeder for this part..
		printf("\nParsed Response of all seeder info%s",fieldvalue);
		strcpy(tempbuffer,fieldvalue);
		i=0;
		do{	
			subfield=strtok(tempbuffer,",");
			printf("\nParsed subfield response of one seeder info%s",subfield);
			fflush(stdout);
			/*if(subfield==NULL){
			break;
			}
			*/
			strcpy(tempbuffer2,subfield);
			subfieldvalue=strtok(NULL,":"); //peer_id
			printf("\nParsed:%s",subfieldvalue);
				fflush(stdout);
			sscanf (subfieldvalue, "%d", &peer_id);
			printf("\nParsed Peer Id%d",peer_id);
			fflush(stdout);
			subfieldvalue=strtok(NULL,":"); // seeder ip
			printf("\nParsed:%s",subfieldvalue);
				fflush(stdout);
			strcpy(seederip,subfieldvalue);
			subfieldvalue=strtok(NULL,":"); // seeder port
			printf("\nParsed:%s",subfieldvalue);
				fflush(stdout);
			sscanf (subfieldvalue, "%d", &seeder_port);
		
			printf("\nParsed Data:");	
			printf("\nPeer_id.%d",peer_id);
			fflush(stdout);
	
			
			printf("\nSeeder Ip: %s",seederip);
			fflush(stdout);
			printf("\nSeeder Port:%d",seeder_port);
			fflush(stdout);
			
			if(head!=NULL){
				
				while(temp->part_num!=part_num){
					temp=temp->next;		
				}
					strcpy(temp->filename,filename);
					temp->part_num=part_num;
					
					seederindex=0;
					while((seederindex<MAX_SEEDS) && (temp->seeder_list[seederindex].seeder_id!=0)){
						seederindex++;
					}
					if(seederindex<MAX_SEEDS){
						temp->seeder_list[seederindex].seeder_id=peer_id;
						inet_pton(AF_INET,seederip, &(temp->seeder_list[seederindex].sd_sin.sin_addr));
						temp->seeder_list[seederindex].sd_sin.sin_port=htons(seeder_port);
					}
					else{
						printf("\nNo Space Available to add extra seeder ... didnot populate the node\n");
					}
			}
		
		}while(subfield!=NULL);
	
		
}

// function used to get latest information from tracker about the part.
void getinfo_part_populate(struct file_part * head,char *trackerIP,unsigned int trackerport,char * filename,int part_num)
{
		struct sockaddr_in trackersock_in;
	 int sock, sinsize;
	 char send_buffer[SOCK_BUFF]={0};
	
	 char receive_buffer[SOCK_BUFF]={0};
	 sinsize = sizeof(trackersock_in);
	 	 memset(&trackersock_in, 0, sizeof(trackersock_in));
	 trackersock_in.sin_family = AF_INET; 

		
		inet_pton(AF_INET,trackerIP, &(trackersock_in.sin_addr));
	  trackersock_in.sin_port=htons(trackerport);
		
		memset(&send_buffer,0,SOCK_BUFF);
  		sprintf(send_buffer,"2$%d$%s$%d$",seq_no,filename,part_num); // 0x02 protocol field
		seq_no++;
  		if(seq_no>=65535)
  			seq_no=0;
  		#ifdef DEBUG
		printf("\nThe Buffer is %s ",send_buffer);
		fflush(stdout);
#endif
 		 /*create socket*/
		if((sock=socket(PF_INET,SOCK_STREAM,0))<0){
			printf("\nFailed to create socket ... \n");
			exit(1);
		}

#ifdef DEBUG
		printf("\nConnecting to Tracker at IP: %s , Port %d ",trackerIP,trackerport);
		fflush(stdout);
#endif
		/*Connect to server*/
		if(connect(sock,(struct sockaddr *)&trackersock_in,sizeof(trackersock_in)) <0){
			printf("\nFailed to connect to tracker ... \n");
			exit(1);		
		}
		/*Send protocol to Tracker*/
		
		send(sock,send_buffer,sizeof(send_buffer),0);
 #ifdef DEBUG
		printf("\nBuffer Sent ... Waiting for response...");
		fflush(stdout);
#endif
recv(sock,receive_buffer,sizeof(receive_buffer),0);
			
#ifdef DEBUG
			printf("\nReceived Buffer for piece..%d.%s",part_num,receive_buffer);
			fflush(stdout);
#endif
			//IF 0xFF is received there is no seeder in the list*****
			if(!strcmp(receive_buffer,"FF")){
				printf("\n Received FF Some error");
			}
			
			//send buffer to update node
			update_listds(head,receive_buffer);
			
		printf("\n node populated");
		fflush(stdout);
		close(sock);
		
		
}


void info_req2track_populate(char *trackerIP,unsigned int trackerport,char * filename, int pieces,int peer_id,struct file_part *head)
{

	struct sockaddr_in trackersock_in;
	 int sock, sinsize;
	 sinsize = sizeof(trackersock_in);
	 char send_buffer[SOCK_BUFF]={0};
	 char receive_buffer[SOCK_BUFF]={0};
	 memset(&trackersock_in, 0, sizeof(trackersock_in));
	 trackersock_in.sin_family = AF_INET; 
  	
  	char * field,*fieldvalue,*subfield,*subfieldvalue;
    char seederip[30];
    int seeder_port;
  	int i=0,node_number=0;
  	int noseederflag=0;
	  inet_pton(AF_INET,trackerIP, &(trackersock_in.sin_addr));
	  trackersock_in.sin_port=htons(trackerport);
 #ifdef DEBUG
  	printf("\nSending Retrieve file Info request to tracker ",trackerIP,trackerport);
  	fflush(stdout);
 #endif 
	do{  
  		memset(&send_buffer,0,SOCK_BUFF);
  		sprintf(send_buffer,"3$%d$%s$%d",seq_no,filename,pieces);   // 0x03 protocol field
  		seq_no++;
  		if(seq_no>=65535)
  			seq_no=0;
#ifdef DEBUG
		printf("\nThe Buffer is %s ",send_buffer);
		fflush(stdout);
#endif
 		 /*create socket*/
		if((sock=socket(PF_INET,SOCK_STREAM,0))<0){
			printf("\nFailed to create socket ... \n");
			exit(1);
		}

#ifdef DEBUG
		printf("\nConnecting to Tracker at IP: %s , Port %d ",trackerIP,trackerport);
		fflush(stdout);
#endif
		/*Connect to server*/
		if(connect(sock,(struct sockaddr *)&trackersock_in,sizeof(trackersock_in)) <0){
			printf("\nFailed to connect to tracker ... \n");
			exit(1);		
		}
		/*Send protocol to Tracker*/
		
		send(sock,send_buffer,sizeof(send_buffer),0);
 #ifdef DEBUG
		printf("\nBuffer Sent ... Waiting for response...");
		fflush(stdout);
#endif
	
		for(node_number=1;node_number<=pieces;node_number++){
		
			recv(sock,receive_buffer,sizeof(receive_buffer),0);
			
#ifdef DEBUG
			printf("\nReceived Buffer for piece %d...%s",node_number,receive_buffer);
			fflush(stdout);
#endif
			//IF 0xFNF is received there is no seeder in the list*****
			if(!strcmp(receive_buffer,"FNF")){
				noseederflag=1;
				break;
			}
			noseederflag=0; //seeder is present
			//send buffer to update node
			update_listds(head,receive_buffer);
			
		}
print_list(head);
		printf("\n List populated");
		fflush(stdout);
		close(sock);
		
		if(noseederflag==1){
			sleep(WAITTIMEFORSEEDER);
		}
	}while(noseederflag==1);
}



 
int joinfile(char * filename,long int fileSize,int noOfParts,int lpsize,crc filecrc_parsed)
{
	FILE *tempfp;
	FILE *fp;
	char newfilename[20];
	char appendedfile[20];
	char * buffer;
	size_t n;
	int i,Partno;
	crc crc_new;
	sprintf(appendedfile,"%s%s","New",filename);
	fp=fopen(appendedfile,"a+");
	 buffer = (char*) malloc (sizeof(char)*PARTSIZE);
	 if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}
	printf("\n****Appending parts and forming: %s****",appendedfile);
	
#ifdef DEBUG
	printf("\nreceived file size  %ldand parts %d and lpsizze%d****",fileSize,noOfParts,lpsize);
	fflush(stdout);
#endif
	
	
	if(lpsize!=0){
	
		for(i=1;i<noOfParts;i++){
		memset(buffer,0,sizeof(buffer));
		sprintf(newfilename,"%s.part.%d",filename,i);
		printf("\nAppending %s to file %s",newfilename,appendedfile);
		fflush(stdout);
		tempfp=fopen(newfilename,"r+");
		n=fread(buffer,1,PARTSIZE,tempfp);
		fwrite(buffer,1,n,fp);
		fclose(tempfp);
		}
		memset(buffer,0,sizeof(buffer));
		sprintf(newfilename,"%s.part.%d",filename,noOfParts);
		printf("\nAppending %s to file %s\n",newfilename,appendedfile);
		tempfp=fopen(newfilename,"r+");
		n=fread(buffer,1,lpsize,tempfp);
		fwrite(buffer,1,lpsize,fp);
		fclose(tempfp);
		fclose(fp);
	}
	else{
	
		sprintf(newfilename,"%s.part.%d",filename,noOfParts);
		printf("\nAppending %s to file %s\n",newfilename,appendedfile);
		tempfp=fopen(newfilename,"r+");
		n=fread(buffer,1,fileSize,tempfp);
		fwrite(buffer,1,fileSize,fp);
		fclose(tempfp);
		fclose(fp);
	
	
	}
free(buffer);

		printf("\nFile created, checking crc ...%s",filename);
		crc_new=calculatecrc(filename);
		if(crc_new==filecrc_parsed)
		{
			printf("\nComplete File is present\n");
			return SUCCESS;
		}
		else{
			printf("\nFILE CRC mismatch\n");
			return FAILURE;
		}

}


void delete_parts(char * filename,int noOfParts)
{
	int i;
	char newfilename[20];
	printf("no of parts = %d\n",noOfParts);
	for(i=1;i<=noOfParts;i++){
		sprintf(newfilename,"%s.part.%d",filename,i);
#ifdef DEBUG
//		printf("Deleting %s...\n",newfilename);
//		fflush(stdout);
#endif
		if(remove(newfilename) != 0)
			printf("Unable to delete %s\n",newfilename);
		else{
			;//printf("Deleted %s\n",newfilename);
		}
	}
}


int leecher_client(struct file_part *head,char *filename,int part_num)
{
	struct file_part *temp = head;
	char newpart[BUFF]={0};
	char buffer[BUFFSIZE],tempbuff[BUFFSIZE];
	char msg[50];
	char p_ack = 'P', n_ack = 'N',s_ack = 'S';
	unsigned long int ip_addr;
	unsigned short int port;
	int sock;
	int no_of_pieces=0,random_piece=0,part_size,no_of_seeders=0;
	int size_counter=0,write_count=0,i;
	size_t bytes_read;
	struct leecher sin;
	pthread_mutex_t safe_leech=PTHREAD_MUTEX_INITIALIZER;
	//calculating number of pieces
#ifdef DEBUG
	printf("\n Filename received in head node :%s and filename %s, part num is%d", head->filename,filename, part_num);
	fflush(stdout);
#endif
	while(temp){
		no_of_pieces++;
		temp=temp->next;
	}
#ifdef DEBUG
	printf("\nNumber of pieces in file: %d",no_of_pieces);
	fflush(stdout);
#endif
	temp = head;
	while(temp->part_num != part_num){
		temp=temp->next;
	}
#ifdef DEBUG
	printf("\nI have to download part: %d",temp->part_num);
	fflush(stdout);
#endif
	//Now temp is at the reqd node.
	if(temp->part_num == no_of_pieces)
		part_size = temp->file_size - (no_of_pieces* PARTSIZE);
	else
		part_size = PARTSIZE;
	
	sprintf(newpart,"%s.part.%d",filename,part_num);
#ifdef DEBUG
	printf("\nNew part name is: %s",newpart);
	fflush(stdout);
#endif
	pthread_mutex_unlock(&safe_leech);
	pthread_mutex_lock(&safe_leech);
	
	temp->data_ptr = fopen(newpart,"ab+");
	
	if(!temp->data_ptr){
		printf("ERROR: file '%s' cannot be created.No space\n",newpart);
		exit(1);
	}
	pthread_mutex_unlock(&safe_leech);
	for(i=0;i<MAX_SEEDS;i++){
		if(temp->seeder_list[i].seeder_id)
			no_of_seeders++;
	}

#ifdef DEBUG
	printf("\nNo of seeders in the seeder list are: %d\n",no_of_seeders);
	fflush(stdout);
#endif

	random_piece = random_select(no_of_seeders);

#ifdef DEBUG
	printf("\nRandom piece to be selected is: %d\n",random_piece);
	fflush(stdout);
#endif


	memset(&sin.lc_sin,0,sizeof(sin.lc_sin));
	sin.lc_sin.sin_family = AF_INET;
	//get seeders address.
	pthread_mutex_lock(&safe_leech);
	
/*	
	ip_addr = ntohl(temp->seeder_list[random_piece].sd_sin.sin_addr.s_addr);
	if((sin.lc_sin.sin_addr.s_addr = htonl(ip_addr)) == INADDR_NONE){//update ip addr
		printf("Cant get IP address of seeder\n");
	}
	port = htons(temp->seeder_list[random_piece].sd_sin.sin_port);
	if((sin.lc_sin.sin_port = htons(port)) == 0){//update port number.
		printf("Can't get Port noumber of seeder\n");
	}
*/
/************************* Need to refine this logic ***********************/
	sin.lc_sin.sin_addr.s_addr=temp->seeder_list[random_piece].sd_sin.sin_addr.s_addr;
	sin.lc_sin.sin_port=temp->seeder_list[random_piece].sd_sin.sin_port;
	
	//memcpy(sin.lc_sin,temp->seeder_list[0].sd_sin,sizeof(temp->seeder_list[random_piece].sd_sin));
	
	inet_ntop(AF_INET, &(sin.lc_sin.sin_addr), tempbuff, BUFFSIZE);
#ifdef DEBUG
	printf("Random Number generated is: %d\n",random_piece);
#endif
	printf("\n Connecting to seeder on IP and Port :%s:%d\n",tempbuff,ntohs(sin.lc_sin.sin_port));
	fflush(stdout);
	
	
	//TODO : exit if no connection is formed
	
	pthread_mutex_unlock(&safe_leech);
	//create socket
	if((sock = socket(PF_INET,SOCK_STREAM,0))<0){
		printf("ERROR:Socket not created\n");
		exit(1);
	}
	//connect socket
	if(connect(sock,(struct sockaddr*)&sin.lc_sin,sizeof(sin.lc_sin))<0){
		printf("ERROR:Unable to connect to seeder\n");
		return CONNECTERROR;
	}
	else{
		printf("Connected to seeder with ID:%d\n",temp->seeder_list[0].seeder_id);
	}
	
	printf("Downloading %s  of size %d ...",newpart,temp->file_size);
	sprintf(msg,"%s,%d%c",filename,part_num,'$');
	send(sock,msg,sizeof(msg),0);//sending the filename and part_num to the seeder
#ifdef DEBUG
	printf("\nSent filename and partnum to seeder\n");
	fflush(stdout);
#endif
//	memset(&buffer,0,sizeof(buffer));
	
/*	bytes_read=recv(sock,buffer,BUFFSIZE,MSG_WAITALL);*/
/*	*/
/*	buffer[bytes_read]='\0';*/
/*	printf("\nReceived :%s\n",buffer);*/
/*	*/
/*	//if(!strcmp(buffer,&p_ack)){*/
/*	printf("\nAck P Received");*/
/*		send(sock,&p_ack,sizeof(p_ack),0);*/
/*		*/
/*		fflush(stdout);*/
/*	//}*/
	//send(sock,msg,sizeof(msg),0);//sending the filename and part_num to the seeder
	
	size_counter=0;
	
	while(size_counter != temp->file_size){
		memset(&buffer,0,sizeof(buffer));
		bytes_read = recv(sock,buffer,BUFFSIZE,0);
#ifdef DEBUG
		printf("bytes read %d  ....\n",bytes_read);
		fflush(stdout);
#endif
		pthread_mutex_lock(&safe_leech);
		if(fwrite(buffer,sizeof(char),bytes_read,temp->data_ptr) == bytes_read){
		//fwrite(buffer,sizeof(char),bytes_read,temp->data_ptr);
			size_counter += bytes_read;
#ifdef DEBUG
			printf("Bytes written to file so far :%d\n",size_counter);
			fflush(stdout);
#endif
			write_count++;
			send(sock,&p_ack,sizeof(p_ack),0);
		}
		
		else {
			size_counter -= bytes_read;
			write_count--;
			printf("ERROR writing to file...\n");
			send(sock,&n_ack,sizeof(n_ack),0);
		}
		pthread_mutex_unlock(&safe_leech);
	/*	if(bytes_read==0)
		{
		break;
		fclose(temp->data_ptr);
		}*/
	}
	fclose(temp->data_ptr);
	close(sock);
	printf("\n Socket closed.. Closing file ptr ... \n");
	fflush(stdout);
	
	pthread_mutex_lock(&safe_leech);
	
	pthread_mutex_unlock(&safe_leech);
	
	pthread_mutex_lock(&safe_leech);
	temp->percent_downloaded = 100.00;
	//temp->download_status = COMPLETE;
	pthread_mutex_unlock(&safe_leech);
	//CRC check
	printf("\n Download Status Set to 100 ... \n");
	fflush(stdout);
	return 0;
}


int main_leecher(char *trackerIP,unsigned int trackerport,char * filename, int pieces,int lpsize, int fileSize,int peer_id,struct file_part *head,crc filecrc_parsed)
{
	int node_number;
	int download_fail=0;	
	int connectflag=0;
	int part_downloadflag=0;
	
#ifdef DEBUG
	printf("\n Main Leecher Started ...");
	fflush(stdout);
#endif
	print_list(head);
	
	printf("\n Peer ID is :%d",peer_id);
	info_req2track_populate(trackerIP,trackerport,filename,pieces,peer_id,head);
	
	for(node_number=1;node_number<=pieces;node_number++)
	{
		part_downloadflag=leecher_client(head,filename,node_number);
		printf("\n Download Flag for node_number %d is %d",node_number,part_downloadflag);
		fflush(stdout);
		if(part_downloadflag==CONNECTERROR)
		{
			part_downloadflag=leecher_client(head,filename,node_number);
			if(part_downloadflag==CONNECTERROR)
			{
				printf("\n Seeder not available... clean up the files");
				exit(1);
				fflush(stdout);
			}
		}
		if(part_downloadflag==SUCCESS){
			send_update2tracker(trackerIP,trackerport,filename, node_number,peer_id); //0x01 partial seeding
			if(node_number!=pieces){
				getinfo_part_populate(head,trackerIP,trackerport,filename,node_number+1); // get latest DS of next part from tracker
			}
		}
	}
#ifdef DEBUG
	print_list(head);
#endif
		if(part_downloadflag==SUCCESS){
			download_fail=joinfile(filename,fileSize,pieces,lpsize,filecrc_parsed);

			if(!download_fail){
				return SUCCESS;
			}
			else{
				return FAILURE;
			}
		}

}


